#ifndef FNT_CUBEDATA_H
#define FNT_CUBEDATA_H
#include <stdint.h>
#include <vector>
#include <array>
#include "image.h"
#include <QString>
class Location;
class AnnotTree;
class LoadThreadShared;


struct CubeId {
	const Location* location; //*
	const AnnotTree* annotTree; //*
	int32_t x0, y0, z0; //*
	int nx, ny, nz; //*

	bool operator==(const CubeId& r) const {
		if (r.location != location) return false;
		if (r.annotTree != annotTree) return false;
		if (r.x0 != x0) return false;
		if (r.y0 != y0) return false;
		if (r.z0 != z0) return false;
		if (r.nx != nx) return false;
		if (r.ny != ny) return false;
		if (r.nz != nz) return false;
		return true;
	}
	int size() const {
		return nx*ny*nz;
	}
};

struct CubeData {

	enum class State {
		Empty = 0,
		Reading,
		Ready,
		Error
	};

	CubeId id;

	LoadThreadShared* lts;
	int refc;
	std::vector<State> states;

	int64_t xsize, ysize, zsize;
	int64_t widthAdj, heightAdj, depthAdj;

	char* data;
	CubeType type;

	int32_t x1() const { return id.x0 + xsize*id.nx; }
	int32_t y1() const { return id.y0 + ysize*id.ny; }
	int32_t z1() const { return id.z0 + zsize*id.nz; }

	int nFinished;
	int nError;

	bool finished() const { return nFinished == id.nx*id.ny*id.nz; }

	CubeData(LoadThreadShared* _lts, const CubeId& _id) :
		id(_id), lts{ _lts }, refc{ 0 }, states(id.size(), State::Empty),
		xsize{ 0 }, ysize{ 0 }, zsize{ 0 },
		widthAdj{ 0 }, heightAdj{ 0 }, depthAdj{ 0 },
		data{ nullptr }, type{ CubeType::Unknown },
		nFinished{ 0 }, nError{ 0 }
	{
	}
	~CubeData() {
		if (data)
			delete[] data;
	}

};
class CubeDataRef {
	friend struct LoadThreadShared;
	friend struct LoadThreadPriv;

	CubeData* _cube;

	CubeDataRef(CubeData* f); // called when locked

	CubeData* operator->() const { return _cube; }

public:

	CubeDataRef() : _cube{ nullptr } { }
	~CubeDataRef() {
		release();
	}

	CubeDataRef(const CubeDataRef& r);
	CubeDataRef& operator=(const CubeDataRef& r);

	CubeDataRef(CubeDataRef&& r) :
		_cube{ r._cube }
	{
		r._cube = nullptr;
	}
	CubeDataRef& operator=(CubeDataRef&& r) {
		std::swap(_cube, r._cube);
		return *this;
	}

	void release();

	bool operator!() const { return !_cube; }
	operator bool() const { return _cube; }

	CubeType type() const;
	int64_t width() const;
	int64_t height() const;
	int64_t depth() const;
	int64_t widthAdj() const;
	int64_t heightAdj() const;
	int64_t depthAdj() const;
	int64_t size(int axis) const;
	const char* data() const;
	const CubeId& cubeId() const;
};

struct CubeXform {
	std::array<double, 3> origin; //*
	std::array<double, 9> direction; //*
	std::array<double, 9> direction_inv;
	std::array<double, 3> resolution;
	bool operator==(const CubeXform& r) const {
		for (int i = 0; i<3; i++)
			if (origin[i] != r.origin[i])
				return false;
		for (int i = 0; i<9; i++)
			if (direction[i] != r.direction[i])
				return false;
		return true;
	}
	CubeXform() :
		origin{ 0.0, 0.0, 0.0 },
		direction{ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 } { }
	CubeXform(const std::array<double, 3>& o, const std::array<double, 9>& d) :
		origin(o),
		direction(d) { }
};
struct Cube {
	CubeDataRef data;
	CubeXform xform;
};
struct Source {
	QString name;
	Location* location; //*
	AnnotTree* annotTree; //*
	std::array<int64_t, 3> size;
	std::array<int64_t, 3> cubesize;
	CubeXform xform;
};


struct LoadThreadInput {
	QString location;
	std::array<double, 3> origin;
	std::array<double, 9> direction;
};
#endif // !FNT_CUBEDATA_H
